#include <drogon/drogon.h>
#include "ServerTimer.h"

namespace OCPP
{
	bool ServerTimer::removeDir(String dir)
	{
		if (StringUtil::startsWith(dir, ".")) return false;
		StringVector files = FileUtil::findFiles(dir);

		for (auto file : files)
		{
			if (file == "." || file == "..") continue;
			String fileName = joinPath(dir, file);
			if (FileUtil::typeOfFile(fileName) == FileUtil::REGULAR_FILE)
			{
				FileUtil::deleteFile(fileName);
			}
			else
			{
				removeDir(fileName);
			}
		}

		return FileUtil::deletFolder(dir);
	}

	String ServerTimer::getUtcDateString()
	{
		return toUtcDateString(Date::now());
	}

	String ServerTimer::getUtcDateTimeString()
	{
		return toUtcDateTimeString(Date::now());
	}

	String ServerTimer::getDateString()
	{
		return toDateString(Date::now());
	}

	String ServerTimer::getDateTimeString()
	{
		return toDateTimeString(Date::now());
	}

	String ServerTimer::toUtcDateString(Date date)
	{
		return date.toCustomedFormattedString("%Y%m%d");
	}
	String ServerTimer::toUtcDateTimeString(Date date)
	{
		return date.toCustomedFormattedString("%Y%m%d%H%M%S");
	}

	String ServerTimer::toDateString(Date date)
	{
		return date.toCustomedFormattedStringLocal("%Y%m%d");
	}
	String ServerTimer::toDateTimeString(Date date)
	{
		return date.toCustomedFormattedStringLocal("%Y%m%d%H%M%S");
	}

	String ServerTimer::toUtcDateTimeYearMonthDayHourString(Date date)
	{
		return date.toCustomedFormattedString("%Y%m%d%H0000");
	}
	String ServerTimer::toUtcDateTimeYearMonthDayHourMinuteString(Date date)
	{
		return date.toCustomedFormattedString("%Y%m%d%H%M00");
	}
	String ServerTimer::toDateTimeYearMonthDayHourString(Date date)
	{
		return date.toCustomedFormattedStringLocal("%Y%m%d%H0000");
	}
	String ServerTimer::toDateTimeYearMonthDayHourMinuteString(Date date)
	{
		return date.toCustomedFormattedStringLocal("%Y%m%d%H%M00");
	}

	Date ServerTimer::dateFromString(String dateTime)
	{
		return Date::fromDbStringLocal(dateTime);
	}

	Date ServerTimer::utcDateFromString(String dateTime)
	{
		return Date::fromDbString(dateTime);
	}

	void ServerTimer::startTimer()
	{
		OCPP_LOG_INFO << "startTimer:" << name << "\n";
		OCPP_LOG_INFO << "dataPath:" << dataPath << "\n";
		OCPP_LOG_INFO << "cachePath:" << cachePath << "\n";
		OCPP_LOG_INFO << "fetchSeconds:" << fetchSeconds << "\n";

		if (cachePath.empty())
		{
			OCPP_LOG_ERROR << "cachePath.empty\n";
			return;
		}

		if (!FileUtil::existFile(cachePath)
			&& !FileUtil::makeDirectory(dataPath))
		{
			OCPP_LOG_ERROR << "can't create path " << cachePath << "\n";
			return;
		}

		String fileName = joinPath(cachePath, name + ".db");
		drogon::app().createDbClient("sqlite3", "", 0, "", "", "", 1, A2U8(fileName), name);

		static std::thread t;
		drogon::app().getLoop()->runAfter(0, [this] {
			if (!this->createDb()) return;
			this->getFetchSeconds();
			t = std::thread([this]() {
				while (true)
				{
					this->doJob();

					if (needClearCache) {
						this->clearCache();
					}
					ServerUtil::WaitSeconds(getFetchSeconds());
				}
				});
			t.detach();
			});
	}

	void ServerTimer::doJob()
	{

	}

	void ServerTimer::clearCache()
	{

	}

	void ServerTimer::db_clear_before_dateTime(Date dateTime)
	{
		db_clear_before_dateTime(dateTime.toDbString());
	}

	void ServerTimer::db_clear_before_dateTime(String dateTime)
	{
		drogon::orm::DbClientPtr dbClient = drogon::app().getDbClient(name);
		if (dbClient)
		{
			try {
				dbClient->execSqlSync(
					"DELETE FROM datetime WHERE timestamp < $1"
					, dateTime
				);
			}
			catch (drogon::orm::DrogonDbException& ex) {
				LOG_ERROR << ex.base().what() << "\n";
			}
			catch (...) {
				LOG_ERROR << "unknow error.\n";
			}
		}
	}

	int ServerTimer::db_insert_dateTime(String dateTime)
	{
		drogon::orm::DbClientPtr dbClient = drogon::app().getDbClient(name);
		if (dbClient)
		{
			try {
				const drogon::orm::Result result = dbClient->execSqlSync(
					"INSERT OR IGNORE INTO datetime (timestamp) VALUES($1)"
					, dateTime
				);
				return result.affectedRows();
			}
			catch (drogon::orm::DrogonDbException& ex) {
				LOG_ERROR << ex.base().what() << "\n";
			}
			catch (...) {
				LOG_ERROR << "unknow error.\n";
			}
		}
		return 0;
	}

	String ServerTimer::db_query_start()
	{
		String timestamp;

		drogon::orm::DbClientPtr dbClient = drogon::app().getDbClient(name);
		if (dbClient)
		{
			try {
				const drogon::orm::Result result = dbClient->execSqlSync(
					"select timestamp from datetime ORDER BY id ASC LIMIT 1;"
				);
				for (auto row : result)
				{
					timestamp = row["timestamp"].as<std::string>();
					break;
				}
			}
			catch (drogon::orm::DrogonDbException& ex) {
				LOG_ERROR << ex.base().what() << "\n";
			}
			catch (...) {
				LOG_ERROR << "unknow error.\n";
			}
		}

		return timestamp;
	}

	std::set<String> ServerTimer::db_query(String startTime, String endTime)
	{
		std::set<String> res;

		drogon::orm::DbClientPtr dbClient = drogon::app().getDbClient(name);
		if (dbClient)
		{
			try {
				const drogon::orm::Result result = dbClient->execSqlSync(
					"select * from datetime where timestamp>=$1 and timestamp<=$2"
					, startTime
					, endTime
				);
				for (auto row : result)
				{
					res.insert(row["timestamp"].as<std::string>());
				}
			}
			catch (drogon::orm::DrogonDbException& ex) {
				LOG_ERROR << ex.base().what() << "\n";
			}
			catch (...) {
				LOG_ERROR << "unknow error.\n";
			}
		}

		return res;
	}

	String ServerTimer::db_query_latest()
	{
		String timestamp;

		drogon::orm::DbClientPtr dbClient = drogon::app().getDbClient(name);
		if (dbClient)
		{
			try {
				const drogon::orm::Result result = dbClient->execSqlSync(
					"select timestamp from datetime ORDER BY id DESC LIMIT 1;"
				);
				for (auto row : result)
				{
					timestamp = row["timestamp"].as<std::string>();
					break;
				}
			}
			catch (drogon::orm::DrogonDbException& ex) {
				LOG_ERROR << ex.base().what() << "\n";
			}
			catch (...) {
				LOG_ERROR << "unknow error.\n";
			}
		}

		return timestamp;
	}

	bool ServerTimer::createDb()
	{
		drogon::orm::DbClientPtr dbClient = drogon::app().getDbClient(name);
		if (dbClient == nullptr) return false;
		try {
			String sql1 = "CREATE TABLE IF NOT EXISTS datetime(\
				id INTEGER PRIMARY KEY,\
				timestamp TIMESTAMP DEFAULT(DATETIME('now', 'localtime')))";
			dbClient->execSqlSync(sql1);
		}
		catch (drogon::orm::DrogonDbException& ex) {
			LOG_ERROR << ex.base().what() << "\n";
			return false;
		}
		catch (...) {
			LOG_ERROR << "unknow error.\n";
			return false;
		}
		return true;
	}
}
